using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using Ziggyware.XNAFont;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace XNAFontCreator
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            foreach (FontFamily ff in System.Drawing.FontFamily.Families)
            {
                comboBox1.Items.Add(ff.Name);
            }

            comboBox1.SelectedIndex = 0;

            listBox1.SelectedIndex = 0;


            UpdateCurrentText();
        }

        FontStyle GetSelectedStyle(string fontFamily)
        {
            FontStyle ret = 0;
            bool bHasSelection = false;

            FontFamily f = null;
            foreach (FontFamily ff in System.Drawing.FontFamily.Families)
            {
                if(ff.Name == fontFamily)
                {
                    f = ff;
                    break;
                }
            }

            if (f == null)
                return ret;

            foreach (string s in listBox1.SelectedItems)
            {
                if (s == "Regular" && f.IsStyleAvailable(FontStyle.Regular))
                {
                    bHasSelection = true;
                    ret |= FontStyle.Regular;
                }
                
                if (s == "Italic" && f.IsStyleAvailable(FontStyle.Italic))
                {
                    bHasSelection = true;
                    ret |= FontStyle.Italic;
                }
                if (s == "Bold" && f.IsStyleAvailable(FontStyle.Bold))
                {
                    bHasSelection = true;
                    ret |= FontStyle.Bold;
                }
                if (s == "Strikeout" && f.IsStyleAvailable(FontStyle.Strikeout))
                {
                    bHasSelection = true;
                    ret |= FontStyle.Strikeout;
                }
                if (s == "Underline" && f.IsStyleAvailable(FontStyle.Underline))
                {
                    bHasSelection = true;
                    ret |= FontStyle.Underline;
                }
            }

            if (!bHasSelection)
            {
                ret = FontStyle.Regular;
            }

            return ret;
        }

        void UpdateCurrentText()
        {

            if (textBox1.Text.Length > 0)
            {
                Font f = new Font(comboBox1.Text, Convert.ToSingle(textBox1.Text), GetSelectedStyle(comboBox1.Text), GraphicsUnit.Pixel);
                textBox3.Font = f;
                textBox3.ForeColor = panel1.BackColor;
                System.Drawing.Color c = System.Drawing.Color.FromArgb((int)(uint)(0xFF000000 | (uint)(System.Drawing.Color.White.ToArgb() - panel1.BackColor.ToArgb())));

                textBox3.BackColor = c;
            }
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            FontFamily f = null;
            foreach (FontFamily ff in System.Drawing.FontFamily.Families)
            {
                if (ff.Name == comboBox1.Text)
                {
                    f = ff;
                    break;
                }
            }

            if (f == null)
                return;

            listBox1.BeginUpdate();
            listBox1.Items.Clear();

            if (f.IsStyleAvailable(FontStyle.Regular))
            {
                listBox1.Items.Add("Regular");
            }
            if (f.IsStyleAvailable(FontStyle.Italic))
            {
                listBox1.Items.Add("Italic");
            }
            if (f.IsStyleAvailable(FontStyle.Bold))
            {
                listBox1.Items.Add("Bold");
            }
            if (f.IsStyleAvailable(FontStyle.Strikeout))
            {
                listBox1.Items.Add("Strikeout");
            }
            if (f.IsStyleAvailable(FontStyle.Underline))
            {
                listBox1.Items.Add("Underline");
            }
            
            
            
            
            listBox1.EndUpdate();


            listBox1.SelectedIndex = 0;

            UpdateCurrentText();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                panel1.BackColor = colorDialog1.Color;
                UpdateCurrentText();
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            UpdateCurrentText();
        }

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateCurrentText();
        }

        static public int MeasureDisplayStringWidth(Graphics graphics, string text,
                                            Font font)
        {
            System.Drawing.StringFormat format = new System.Drawing.StringFormat();
            
            System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0,
                                                                          1000, 1000);
            System.Drawing.CharacterRange[] ranges = 
                                       { new System.Drawing.CharacterRange(0, 
                                                               text.Length) };
            System.Drawing.Region[] regions = new System.Drawing.Region[1];

            format.SetMeasurableCharacterRanges(ranges);

            format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

            regions = graphics.MeasureCharacterRanges(text, font, rect, format);
            rect = regions[0].GetBounds(graphics);

            return (int)(rect.Right);
        }

        static public int MeasureDisplayStringLeft(Graphics graphics, string text,
                                            Font font)
        {
            System.Drawing.StringFormat format = new System.Drawing.StringFormat();

            System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0,
                                                                          1000, 1000);
            System.Drawing.CharacterRange[] ranges = 
                                       { new System.Drawing.CharacterRange(0, 
                                                               text.Length) };
            System.Drawing.Region[] regions = new System.Drawing.Region[1];

            format.SetMeasurableCharacterRanges(ranges);

            format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

            regions = graphics.MeasureCharacterRanges(text, font, rect, format);
            rect = regions[0].GetBounds(graphics);

            return (int)rect.Left;
        }


        private void button2_Click(object sender, EventArgs e)
        {
            GraphicsDevice graphicsDevice = CreateXNAGraphicsDevice();

            

            string sChars = "abcdefghijklmnopqrstuvwxyz";
            sChars +=       "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            sChars +=       "1234567890-=";
            sChars +=       "`~!@#$%^&*()_+";
            sChars +=       "[]{};':\",.<>/?\\|";
            sChars += " ";

            Font f = textBox3.Font;
            float fHeight = f.GetHeight(textBox3.CreateGraphics());
            int iHeight = (int)Math.Ceiling((double)fHeight)+1;

            int iPadding = 1;
            int iCurrentX = iPadding;
            int iCurrentY = iPadding;

            int iSize = 64;

            while (true)
            {
                iCurrentX = iPadding;
                iCurrentY = iPadding;
                bool bOK = true;
                foreach (char c in sChars)
                {
                    int iWidth = MeasureDisplayStringWidth(textBox3.CreateGraphics(), c.ToString(), f);

                    if (iCurrentX + iWidth + iPadding > iSize)
                    {
                        iCurrentX = iPadding;
                        iCurrentY += iHeight + iPadding;
                        if (iCurrentY + iHeight + iPadding > iSize)
                        {
                            bOK = false;
                            break;
                        }
                    }
                    iCurrentX += iWidth + iPadding;
                }
                if (bOK)
                    break;
                else
                {
                    iSize = iSize * 2;
                }
            }

            Texture2D t = new Texture2D(graphicsDevice, iSize, iSize, 1, TextureUsage.None, SurfaceFormat.Color);

            uint[] d = new uint[t.Width * t.Height];

            t.GetData<uint>(d);

            //fill with black
            for (int x = 0; x < d.Length; x++)
                d[x] = 0x00000000;


            iCurrentX = iPadding;
            iCurrentY = iPadding;
            System.Collections.Generic.List<OutputElement> outputList = new System.Collections.Generic.List<OutputElement>();

            foreach (char c in sChars)
            {
                int iWidth = MeasureDisplayStringWidth(textBox3.CreateGraphics(), c.ToString(), f);
                int iLeft = MeasureDisplayStringLeft(textBox3.CreateGraphics(), c.ToString(), f);

                if (iCurrentX + iWidth + iPadding > t.Width)
                {
                    iCurrentX = iPadding;
                    iCurrentY += iHeight + iPadding;
                    if (iCurrentY + iHeight + iPadding > t.Height)
                    {
                        MessageBox.Show("Need larger Texture");
                        return;
                    }
                }
                Bitmap bmp = new Bitmap(iWidth,
                    iHeight, 
                    System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                Graphics bmpGraphics = Graphics.FromImage(bmp);


                SolidBrush b = new SolidBrush(panel1.BackColor);
                
                bmpGraphics.DrawString(c.ToString(), f, b, 0, 0);

                for(int x=0;x<iWidth;x++)
                    for(int y=0;y<iHeight;y++)
                    {
                        System.Drawing.Color clr = bmp.GetPixel(x, y);

                        d[iCurrentX+x + (iCurrentY+y)*t.Width] = (uint) clr.ToArgb();
                    }

                OutputElement oe;
                oe.c = c;
                oe.v = iCurrentX+iPadding+iLeft;
                oe.u = iCurrentY+iPadding;
                oe.Width = iWidth-iLeft;

                outputList.Add(oe);

                iCurrentX += iWidth + iPadding;
            }

            t.SetData<uint>(d);

            string strFileName = textBox2.Text;


            Fonts font = new Fonts();
            font.Texture = t;
            
            font.FontFamily = f.FontFamily.GetName(0);

            font.Height = iHeight;
            font.Kerning = 0;

            foreach (OutputElement oe in outputList)
            {
                font.UVData.Add(oe.c, new Fonts.XNAFontUV(oe.u, oe.v, oe.c, oe.Width));
            }

            font.SaveFont(strFileName);
            
            
            t.Dispose();

            graphicsDevice.Dispose();

            MessageBox.Show("Font created successfully");
        }

        private GraphicsDevice CreateXNAGraphicsDevice()
        {
            
            GraphicsDevice graphicsDevice;
            PresentationParameters presentParams = new PresentationParameters();
            presentParams.BackBufferHeight = 512;
            presentParams.BackBufferWidth = 512;
            presentParams.BackBufferFormat = Microsoft.Xna.Framework.Graphics.SurfaceFormat.Color;
            presentParams.BackBufferCount = 1;
            presentParams.DeviceWindowHandle = this.Handle;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.AutoDepthStencilFormat = DepthFormat.Unknown;
            presentParams.IsFullScreen = false;
            presentParams.MultiSampleType = MultiSampleType.None;
            presentParams.PresentationInterval = PresentInterval.Immediate;
            presentParams.PresentOptions = PresentOptions.None;
            presentParams.SwapEffect = SwapEffect.Discard;

            graphicsDevice = new Microsoft.Xna.Framework.Graphics.GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                DeviceType.Hardware, this.Handle, new PresentationParameters());
            return graphicsDevice;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.AddExtension = true;
            ofd.CheckFileExists = false;
            ofd.DefaultExt = "DDS";
            ofd.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
            ofd.Multiselect = false;
            ofd.RestoreDirectory = false;
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                textBox2.Text = ofd.FileName;
            }
        }
    }

    [Serializable]
    struct OutputElement
    {
        public char c;
        public int u, v;
        public int Width;
    }
}